package com.swdegao.iot.netty.server.qianbaotongplushandler;

import lombok.*;
import org.apache.poi.ss.formula.functions.T;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Objects;

/**
 * 千宝通主动上报协议数据模型类
 *
 * @author TsingYu
 * @version 1.0
 * @date 2024/11/27 15:00
 */
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class QianBaoTongProactiveModel<T> {

    /**
     * 起始字符
     */
    private byte start;
    /**
     * 长度
     */
    private int length;
    /**
     * 长度（原始值）
     */
    private byte[] lengthRaw;
    /**
     * 控制域
     */
    private ControlCode controlCode;
    /**
     * 地址域
     */
    private Address address;
    /**
     * 应用层功能码
     */
    private Afn afn;
    /**
     * 帧序列域
     */
    private Seq seq;
    /**
     * 数据单元标识
     */
    private Fn fn;
    /**
     * 数据单元
     */
    private T dataUnit;
    /**
     * 数据单元（原始值）
     */
    private byte[] dataUnitRaw;
    /**
     * 校验和
     */
    private byte checkSum;
    /**
     * 结束字符
     */
    private byte end;

    /**
     * 用model和给定的构造一个对象
     *
     * @param model 不为null
     */
    public QianBaoTongProactiveModel(QianBaoTongProactiveModel<Object> model, T dataUnit) {
        this.start = model.getStart();
        this.length = model.getLength();
        this.lengthRaw = model.getLengthRaw();
        this.controlCode = model.getControlCode();
        this.address = model.getAddress();
        this.afn = model.getAfn();
        this.seq = model.getSeq();
        this.fn = model.getFn();
        this.dataUnit = dataUnit;
        this.dataUnitRaw = model.getDataUnitRaw();
        this.checkSum = model.getCheckSum();
        this.end = model.getEnd();
    }

    /**
     * 控制码
     */
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class ControlCode {
        /**
         * 原始数据
         */
        private byte[] raw;
        /**
         * 传输方向位 DIR<br>
         * DIR=0：表示此帧报文是由主站发出的下行报文；<br>
         * DIR=1：表示此帧报文是由终端发出的上行报文。
         */
        private byte dir;
        /**
         * 启动标志位 PRM<br>
         * PRM=1：表示此帧报文来自启动站；<br>
         * PRM=0：表示此帧报文来自从动站。
         */
        private byte prm;
        /**
         * 功能码
         */
        private byte funcCode;
        /**
         * 功能码含义
         */
        private String funcCodeMeaning;
    }

    /**
     * 地址域
     */
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class Address {
        /**
         * 地址最小长度
         */
        private final static int ADDRESS_MIN_LENGTH = 4;
        /**
         * 终端地址最大值
         */
        public final static int TERMINAL_ADDRESS_MAX_VALUE = 0xFFFF;
        /**
         * 无效终端地址值
         */
        public final static int TERMINAL_ADDRESS_INVALID_VALUE = 0x0000;
        /**
         * 主站地址和组地址标志默认值
         */
        public final static byte ADDRESS_FLAG_DEFAULT = 0x00;
        /**
         * 主站地址和组地址标志二进制最低位为1表示是系统广播地址
         */
        public final static int ADDRESS_FLAG_LOWEST_BIT = 1;


        /**
         * 根据集中器地址创建地址对象
         *
         * @param address               集中器地址
         * @param isSystemBroadcastFlag 是否是系统广播地址
         */
        public Address(String address, boolean isSystemBroadcastFlag) {
            if (address == null || address.length() < ADDRESS_MIN_LENGTH) {
                throw new IllegalArgumentException("非法的地址参数");
            }
            this.address = address;
            this.areaCode = address.substring(0, 4);
            this.terminalAddr = Integer.parseInt(address.substring(4));
            if (isSystemBroadcastFlag && this.terminalAddr == TERMINAL_ADDRESS_MAX_VALUE) {
                this.addressFlag = ADDRESS_FLAG_LOWEST_BIT;
            } else {
                this.addressFlag = ADDRESS_FLAG_DEFAULT;
            }

        }

        /**
         * 原始数据
         */
        private byte[] raw;
        /**
         * 地址
         */
        private String address;
        /**
         * 行政区划码
         */
        private String areaCode;
        /**
         * 行政区划码（原始值）
         */
        private byte[] areaCodeRaw;
        /**
         * 终端地址
         */
        private int terminalAddr;
        /**
         * 终端地址（原始值）
         */
        private byte[] terminalAddrRaw;
        /**
         * 主站地址和组地址标志
         */
        private byte addressFlag;
        /**
         * 是否是系统广播地址
         */
        private boolean systemBroadcastFlag;
        /**
         * 无效地址
         */
        private boolean invalidAddress;
    }

    /**
     * 应用层功能码
     */
    @ToString
    public enum Afn {
        /**
         * 确认∕否认
         */
        CONFIRM((byte) 0x00, "确认∕否认"),
        /**
         * 复位命令
         */
        RESET((byte) 0x01, "复位命令"),
        /**
         * 链路接口检测
         */
        TEST((byte) 0x02, "链路接口检测"),
        /**
         * 设置集中器地址
         */
        CONCENTRATOR((byte) 0x04, "设置集中器地址"),
        /**
         * 设置参数
         */
        SETTING((byte) 0x84, "设置参数"),
        /**
         * 控制命令
         */
        CONTROL((byte) 0x85, "控制命令"),
        /**
         * 查询参数
         */
        QUERY((byte) 0x8A, "查询参数"),
        /**
         * 请求 1类数据（实时数据）
         */
        REQUESTING((byte) 0x8C, "请求1类数据（实时数据）");

        /**
         * 应用功能码
         */
        private final byte fnCode;
        /**
         * 应用功能名称
         */
        private final String fnName;

        Afn(byte fnCode, String fnName) {
            this.fnCode = fnCode;
            this.fnName = fnName;
        }

        public byte getFnCode() {
            return fnCode;
        }

        public String getFnName() {
            return fnName;
        }

        public static Afn getAfnByFnCode(byte fnCode) {
            for (Afn afn : Afn.values()) {
                if (Objects.equals(fnCode, afn.getFnCode())) {
                    return afn;
                }
            }
            return null;
        }
    }

    /**
     * 帧序列域
     */
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class Seq {
        /**
         * 原始值
         */
        private byte[] raw;
        /**
         * 首帧标志，置“1”，报文的第一帧。FIR、FIN组合状态所表示的含义如下表<br>
         * <table border="1">
         *     <tr>
         *         <th>FIR</th>
         *         <th>FIN</th>
         *         <th>应用说明</th>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>多帧：中间帧</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>多帧：结束帧</td>
         *     </tr>
         *     <tr>
         *         <td>1</td>
         *         <td>0</td>
         *         <td>多帧：第 1帧，有后续帧。</td>
         *     </tr>
         *     <tr>
         *         <td>1</td>
         *         <td>1</td>
         *         <td>单帧</td>
         *     </tr>
         * </table>
         */
        private byte fir;
        /**
         * 末帧标志，置“1”，报文的最后一帧。FIR、FIN组合状态所表示的含义如下表<br>
         * <table border="1">
         *     <tr>
         *         <th>FIR</th>
         *         <th>FIN</th>
         *         <th>应用说明</th>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>多帧：中间帧</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>多帧：结束帧</td>
         *     </tr>
         *     <tr>
         *         <td>1</td>
         *         <td>0</td>
         *         <td>多帧：第 1帧，有后续帧。</td>
         *     </tr>
         *     <tr>
         *         <td>1</td>
         *         <td>1</td>
         *         <td>单帧</td>
         *     </tr>
         * </table>
         */
        private byte fin;
        /**
         * 请求确认标志位，在所收到的报文中，CON位置“1”，表示需要对该帧报文进行确认；置“0”，表示不
         * 需要对该帧报文进行确认。
         */
        private byte con;
        /**
         * 帧序号
         */
        private byte seq;
    }

    /**
     * 数据单元标识
     */
    @Data
    @ToString
    @AllArgsConstructor
    @Builder
    public static class Fn {
        /**
         * 原始值
         */
        private byte[] raw;
        /**
         * 由信息点标识DA（原始值）
         */
        private byte[] daRaw;
        /**
         * 由信息点标识DA1（二进制字符串）
         */
        private String da1;
        /**
         * 由信息点标识DA2（二进制字符串）
         */
        private String da2;
        /**
         * 信息类标识DT（原始值）
         */
        private byte[] dtRaw;
        /**
         * 信息类标识DT1（二进制字符串）
         */
        private String dt1;
        /**
         * 信息类标识DT2（二进制字符串）
         */
        private String dt2;
        /**
         * pn取值范围-1~64，当pn为-1时，DA1和DA2为0xFF；当pn为0时，DA1和DA2为0x00；其余值按规则对应。<br>
         * DA2对位表示1～8个信息点组，DA1对位表示某一信息点组的1～8个信息点，共同构成信息点pn（n＝1～64）。
         * 当DA1和DA2均为0时，表示终端信息点p0。当DA1和DA2均为0FFH时，表示全体信息点。
         */
        private int pn;
        /**
         * DT2以二进制码表示0～30个信息类组，DT1对位表示某一信息类组的1～8种信息类型，共同构成信息类Fn（n＝1～248）。
         * 信息点pn对应于不同的信息类Fn可以是测量点号、控制轮次和任务号等。
         */
        private int fn;

        /**
         * pn最大值
         */
        private final static int MAX_PN = 64;
        /**
         * fn最大值
         */
        private final static int MAX_FN = 248;
        /**
         * 数据单元标识 Fn 字节长度
         */
        private final static int FN_BYTE_LENGTH = 4;

        /**
         * 请使用{@link #Fn(int, int)}或者{@link #Fn(byte[])}构造函数
         */
        @Deprecated
        public Fn() {
        }

        /**
         * 根据所给的pn、fn创建一个Fn对象
         *
         * @param pn pn取值范围-1~64，当pn为-1时，DA1和DA2为0xFF；当pn为0时，DA1和DA2为0x00；其余值按规则对应。<br>
         *           DA2对位表示1～8个信息点组，DA1对位表示某一信息点组的1～8个信息点，共同构成信息点pn（n＝1～64）。
         *           当DA1和DA2均为0时，表示终端信息点p0。当DA1和DA2均为0FFH时，表示全体信息点。
         * @param fn DT2以二进制码表示0～30个信息类组，DT1对位表示某一信息类组的1～8种信息类型，共同构成信息类Fn（n＝1～248）。
         *           信息点pn对应于不同的信息类Fn可以是测量点号、控制轮次和任务号等。
         */
        public Fn(int pn, int fn) {
            this.pn = pn;
            this.fn = fn;
            if (pn < -1 || pn > MAX_PN || fn < 1 || fn > MAX_FN) {
                throw new IllegalArgumentException("非法参数：pn取值范围-1~64，fn取值范围1～248。");
            }
            if (pn == -1) {
                this.da1 = "11111111";
                this.da2 = "11111111";
            } else if (pn == 0) {
                this.da1 = "00000000";
                this.da2 = "00000000";
            } else {
                // da1的Dn位
                int da1Dn = pn % 8 - 1;
                // da2的Dn位
                int da2Dn = pn % 8 == 0 ? (pn / 8 - 1) : pn / 8;
                String initialString = "00000000";
                StringBuilder da1 = new StringBuilder(initialString);
                // 将序号为n的字符改为1，7-n是因为从右往左的索引是0~7
                da1.setCharAt(7 - da1Dn, '1');
                this.da1 = da1.toString();
                StringBuilder da2 = new StringBuilder(initialString);
                // 将序号为n的字符改为1，7-n是因为从右往左的索引是0~7
                da2.setCharAt(7 - da2Dn, '1');
                this.da2 = da2.toString();
            }
            byte[] da = new byte[2];
            da[0] = (byte) Integer.parseInt(this.da1, 2);
            da[1] = (byte) Integer.parseInt(this.da2, 2);
            this.daRaw = da;
            // dt1的Dn位
            int dt1Dn = fn % 8 - 1;
            // dt2的十进制值
            int dt2 = fn % 8 == 0 ? (fn / 8 - 1) : fn / 8;
            String initialString = "00000000";
            StringBuilder dt1 = new StringBuilder(initialString);
            // 将序号为n的字符改为1，7-n是因为从右往左的索引是0~7
            dt1.setCharAt(7 - dt1Dn, '1');
            this.dt1 = dt1.toString();
            this.dt2 = String.format("%8s", Integer.toBinaryString(dt2)).replace(' ', '0');
            byte[] dt = new byte[2];
            dt[0] = (byte) Integer.parseInt(this.dt1, 2);
            dt[1] = (byte) dt2;
            this.dtRaw = dt;
            byte[] raw = new byte[FN_BYTE_LENGTH];
            System.arraycopy(this.daRaw, 0, raw, 0, 2);
            System.arraycopy(this.dtRaw, 0, raw, 2, 2);
            this.raw = raw;
        }

        /**
         * 根据Fn的原始值解析并构造Fn对象
         *
         * @param raw Fn的原始值
         */
        public Fn(byte[] raw) {
            if (raw == null || raw.length != FN_BYTE_LENGTH) {
                throw new IllegalArgumentException(String.format("无效的参数，Fn长度应该为 %s", FN_BYTE_LENGTH));
            }
            this.raw = raw;
            // 信息点标识DA
            byte[] daByte = Arrays.copyOfRange(raw, 0, 2);
            this.daRaw = daByte;
            // 转二进制
            String da1 = String.format("%8s", Integer.toBinaryString(daByte[0] & 0xFF)).replace(' ', '0');
            // 转二进制
            String da2 = String.format("%8s", Integer.toBinaryString(daByte[1] & 0xFF)).replace(' ', '0');
            this.da1 = da1;
            this.da2 = da2;
            // 信息类标识DT
            byte[] dtByte = Arrays.copyOfRange(raw, 2, 4);
            this.dtRaw = dtByte;
            // 转二进制
            String dt1 = String.format("%8s", Integer.toBinaryString(dtByte[0] & 0xFF)).replace(' ', '0');
            // 转二进制
            String dt2 = String.format("%8s", Integer.toBinaryString(dtByte[1] & 0xFF)).replace(' ', '0');
            this.dt1 = dt1;
            this.dt2 = dt2;
        }

        @Override
        public boolean equals(Object object) {
            if (this == object) {
                return true;
            }
            if (object == null || getClass() != object.getClass()) {
                return false;
            }
            Fn fn = (Fn) object;
            return Objects.deepEquals(raw, fn.raw);
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(raw);
        }
    }


    /**
     * 设备档案
     */
    @Data
    @ToString
    public static class DeviceProfile {

        private byte[] raw;
        /**
         * 表序号
         */
        private int seq;
        /**
         * 表序号（原始值）
         */
        private byte[] seqRaw;
        /**
         * 表号<br>
         * 水电气表通信地址
         */
        private String meterNumber;
        /**
         * 表号（原始值）<br>
         * 水电气表通信地址
         */
        private byte[] meterNumberRaw;
        /**
         * 测量点性质
         */
        private MeasuringPointProperties measuringPointProperties;
        /**
         * 测量点性质（原始值）
         */
        private byte[] measuringPointPropertiesRaw;
        /**
         * 接线方式
         */
        private WiringType wiringType;
        /**
         * 接线方式（原始值）
         */
        private byte[] wiringTypeRaw;
        /**
         * 电表费率号
         */
        private MeterFeeRateNumber meterFeeRateNumber;
        /**
         * 电表费率号（原始值）
         */
        private byte[] meterFeeRateNumberRaw;
        /**
         * 表类型代码
         */
        private MeterTypeCode meterTypeCode;
        /**
         * 表类型代码（原始值）
         */
        private byte[] meterTypeCodeRaw;
        /**
         * 线路编号<br>
         * 双字节 BCD 码，采用整数格式型传送；
         * 485/MBUS 总线通信方式时表示 485 总线物理通道号。
         */
        private LineNumber lineNumber;
        /**
         * 线路编号（原始值）<br>
         * 双字节 BCD 码，采用整数格式型传送；
         * 485/MBUS 总线通信方式时表示 485 总线物理通道号。
         */
        private byte[] lineNumberRaw;
        /**
         * 表箱编号<br>
         * 双字节整数，整数格式型。
         */
        private short boxNumber;
        /**
         * 表箱编号（原始值）<br>
         * 双字节整数，整数格式型。
         */
        private byte[] boxNumberRaw;
        /**
         * 采集模块编号<br>
         * 同一采集模块编号的水电气为一带N的采
         * 集模块所抄读，采集模块编号为零的水电
         * 气则表示此表与集中器直接相连，采集模
         * 块编号可以作为采集模块通信地址。
         */
        private String collectingModuleNumber;
        /**
         * 采集模块编号（原始值）<br>
         * 同一采集模块编号的水电气为一带N的采
         * 集模块所抄读，采集模块编号为零的水电
         * 气则表示此表与集中器直接相连，采集模
         * 块编号可以作为采集模块通信地址。
         */
        private byte[] collectingModuleNumberRaw;

    }

    /**
     * 测量点性质（1字节）
     */
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class MeasuringPointProperties {
        private MeasuringPointPropertiesG g;
        private MeasuringPointPropertiesH h;
    }

    /**
     * 接线方式（1字节）
     */
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class WiringType {
        private WiringTypeG g;
        private WiringTypeS s;
    }

    /**
     * 电表费率号（1字节）
     */
    @Data
    @ToString
    @AllArgsConstructor
    public static class MeterFeeRateNumber {
        /**
         * G3、G2、G1、G0组成的二进制字符串对应的byte值
         * <table>
         *     <tr>
         *         <th>G3</th>
         *         <th>G2</th>
         *         <th>G1</th>
         *         <th>G0</th>
         *         <th>含义</th>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>对应总电量</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>保留</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>0</td>
         *         <td>对应总、1、2电量</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>1</td>
         *         <td>对应总、1、2、3电量</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>对应总、1、2、3、4电量</td>
         *     </tr>
         *     <tr>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>保留</td>
         *     </tr>
         * </table>
         */
        private byte g;
        /**
         * G3、G2、G1、G0组成的二进制字符串
         */
        private String gBinary;
        /**
         * S3、S2、S1、S0组成的二进制字符串对应的byte值
         * <table>
         *     <tr>
         *         <th>S3</th>
         *         <th>S2</th>
         *         <th>S1</th>
         *         <th>S0</th>
         *         <th>含义</th>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>单费率电表</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>启用的费率方案号1</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>0</td>
         *         <td>启用的费率方案号2</td>
         *     </tr>
         *     <tr>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>…</td>
         *     </tr>
         *     <tr>
         *         <td>1</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>启用的费率方案号8</td>
         *     </tr>
         *     <tr>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>…</td>
         *         <td>保留</td>
         *     </tr>
         * </table>
         */
        private byte s;
        /**
         * S3、S2、S1、S0组成的二进制字符串
         */
        private String sBinary;

        /**
         * 请使用{@link #MeterFeeRateNumber(byte, byte)}构造函数创建对象
         */
        @Deprecated
        public MeterFeeRateNumber() {

        }

        /**
         * 根据给定的g、s构造一个电表费率号对象
         *
         * @param g G3、G2、G1、G0组成的二进制字符串对应的byte值
         * @param s S3、S2、S1、S0组成的二进制字符串对应的byte值
         */
        public MeterFeeRateNumber(byte g, byte s) {
            int maxValue = 15;
            if (g > maxValue || g < 0 || s > maxValue || s < 0) {
                throw new IllegalArgumentException("g、s只能是0~15的整数");
            }
            this.g = g;
            this.s = s;
            this.gBinary = String.format("%4s", Integer.toBinaryString(g)).replace(' ', '0');
            this.sBinary = String.format("%4s", Integer.toBinaryString(s)).replace(' ', '0');
        }
    }

    /**
     * 表类型代码（1字节）
     */
    @Data
    @ToString
    public static class MeterTypeCode {
        /**
         * G1、G0组成的二进制字符串对应的byte值
         * <table>
         *     <tr>
         *         <th>G1</th>
         *         <th>G0</th>
         *         <th>含义</th>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>0</td>
         *         <td>不确定</td>
         *     </tr>
         *     <tr>
         *         <td>0</td>
         *         <td>1</td>
         *         <td>A相</td>
         *     </tr>
         *     <tr>
         *         <td>1</td>
         *         <td>0</td>
         *         <td>B相</td>
         *     </tr>
         *     <tr>
         *         <td>1</td>
         *         <td>1</td>
         *         <td>C相</td>
         *     </tr>
         * </table>
         */
        private byte g;
        /**
         * G1、G0组成的二进制字符串
         */
        private String gBinary;
        /**
         * S的定义：S=0，不带拉闸，S=1，带拉闸。
         */
        private byte s;
        /**
         * H的定义：H =0，普通用户，H =1，重点用户。
         */
        private byte h;
        /**
         * K的定义：K =0，不选抄，K =1，选抄，即不响应对该电表的数据抄读或参数设置命令。
         */
        private byte k;
        /**
         * F的定义：F =0，允许断电，F =1，不允许断电。
         */
        private byte f;

        /**
         * 请使用{@link #MeterTypeCode(byte, byte, byte, byte, byte)}构造函数创建对象
         */
        @Deprecated
        public MeterTypeCode() {
        }

        /**
         * @param g G1、G0组成的二进制字符串对应的byte值，范围0~3
         * @param s S的定义：S=0，不带拉闸，S=1，带拉闸。
         * @param h H的定义：H =0，普通用户，H =1，重点用户。
         * @param k K的定义：K =0，不选抄，K =1，选抄，即不响应对该电表的数据抄读或参数设置命令。
         * @param f F的定义：F =0，允许断电，F =1，不允许断电。
         */
        public MeterTypeCode(byte g, byte s, byte h, byte k, byte f) {
            int maxValue = 4;
            if (g > maxValue || g < 0 || s * s != s || h * h != h || k * k != k || f * f != f) {
                throw new IllegalArgumentException("g只能是0~3的整数，s、h、k、f只能是0或1");
            }
            this.g = g;
            this.s = s;
            this.h = h;
            this.k = k;
            this.f = f;
            this.gBinary = String.format("%2s", Integer.toBinaryString(g)).replace(' ', '0');
        }
    }

    /**
     * 线路编号（1字节）
     */
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class LineNumber {
        /**
         * 主线路编号
         */
        private byte primaryLineNumber;
        /**
         * 第一级分支线路
         */
        private byte firstLineNumber;
        /**
         * 第二级分支线路
         */
        private byte secondLineNumber;
        /**
         * 第三级分支线路
         */
        private byte thirdLineNumber;
    }

    /**
     * 集中器信息
     */
    @Data
    @ToString
    public static class Concentrator {
        private byte[] raw;
        /**
         * 集中器时钟
         */
        private String concentratorClock;
        /**
         * 集中器时钟（原始值）
         */
        private byte[] concentratorClockRaw;
        /**
         * 软件版本
         */
        private String softwareVer;
        /**
         * 软件版本（原始值）
         */
        private byte[] softwareVerRaw;
        /**
         * 硬件件版本
         */
        private String hardwareVer;
        /**
         * 硬件件版本（原始值）
         */
        private byte[] hardwareVerRaw;

    }


    /**
     * 测量点性质G
     */
    public enum MeasuringPointPropertiesG {
        /**
         * 普通电表
         */
        ORDINARY_ELECTRICITY_METERS((byte) 0, (byte) 0, (byte) 0, "普通电表"),
        /**
         * 简易多功能电表
         */
        SIMPLE_MULTI_FUNCTION_METER((byte) 1, (byte) 0, (byte) 0, "简易多功能电表"),
        /**
         * 多功能总电表
         */
        MULTI_FUNCTION_MASTER_METER((byte) 0, (byte) 1, (byte) 0, "多功能总电表"),
        /**
         * 冷水表
         */
        COLD_WATER_METER((byte) 1, (byte) 1, (byte) 0, "冷水表"),
        /**
         * 热水表
         */
        HOT_WATER_METER((byte) 0, (byte) 0, (byte) 1, "热水表"),
        /**
         * 总水表
         */
        MASTER_METER((byte) 1, (byte) 0, (byte) 1, "总水表"),
        /**
         * 气表
         */
        GAS_METER((byte) 0, (byte) 1, (byte) 1, "气表"),
        /**
         * 保留
         */
        RESERVE((byte) 1, (byte) 1, (byte) 1, "保留");

        private final byte g0;
        private final byte g1;
        private final byte g2;
        /**
         * 表类型
         */
        private final String type;


        MeasuringPointPropertiesG(byte g0, byte g1, byte g2, String type) {
            this.g0 = g0;
            this.g1 = g1;
            this.g2 = g2;
            this.type = type;
        }

        public byte getG0() {
            return g0;
        }

        public byte getG1() {
            return g1;
        }

        public byte getG2() {
            return g2;
        }

        public String getType() {
            return type;
        }

        /**
         * 根据g0、g1、g2获取相应信息
         *
         * @param g0 g0
         * @param g1 g1
         * @param g2 g2
         * @return 相应信息
         */
        public static MeasuringPointPropertiesG getByGn(byte g0, byte g1, byte g2) {
            for (MeasuringPointPropertiesG pointProperties : MeasuringPointPropertiesG.values()) {
                if (g0 == pointProperties.getG0() && g1 == pointProperties.getG1() && g2 == pointProperties.getG2()) {
                    return pointProperties;
                }
            }
            return null;
        }
    }

    /**
     * 测量点性质H
     */
    public enum MeasuringPointPropertiesH {
        /**
         * 预付表
         */
        PREPAID_METER((byte) 0, (byte) 0, (byte) 0, "预付表"),
        /**
         * 预付费表（金额）
         */
        AMOUNT_PREPAID_METER((byte) 1, (byte) 0, (byte) 0, "预付费表（金额）"),
        /**
         * 非预付费表
         */
        NON_PREPAID_METER((byte) 0, (byte) 1, (byte) 0, "非预付费表"),
        /**
         * 485表
         */
        METER_485((byte) 1, (byte) 1, (byte) 0, "485表"),
        /**
         * MBUS表
         */
        MBUS_METER((byte) 0, (byte) 0, (byte) 1, "MBUS表"),
        /**
         * 载波表
         */
        CARRIER_WAVE_METER((byte) 1, (byte) 0, (byte) 1, "载波表");

        private final byte h0;
        private final byte h1;
        private final byte h2;
        /**
         * 表类型
         */
        private final String type;

        MeasuringPointPropertiesH(byte h0, byte h1, byte h2, String type) {
            this.h0 = h0;
            this.h1 = h1;
            this.h2 = h2;
            this.type = type;
        }

        public byte getH0() {
            return h0;
        }

        public byte getH1() {
            return h1;
        }

        public byte getH2() {
            return h2;
        }

        public String getType() {
            return type;
        }

        /**
         * 根据h0、h1、h2获取相应信息
         *
         * @param h0 h0
         * @param h1 h1
         * @param h2 h2
         * @return 相应信息
         */
        public static MeasuringPointPropertiesH getByHn(byte h0, byte h1, byte h2) {
            for (MeasuringPointPropertiesH pointProperties : MeasuringPointPropertiesH.values()) {
                if (h0 == pointProperties.getH0() && h1 == pointProperties.getH1() && h2 == pointProperties.getH2()) {
                    return pointProperties;
                }
            }
            return null;
        }
    }

    /**
     * 接线方式G
     */
    public enum WiringTypeG {
        /**
         * 单相表
         */
        SINGLE_PHASE_METER((byte) 0, "单相表"),
        /**
         * 三相表
         */
        THREE_PHASE_METER((byte) 1, "三相表");

        private final byte g;
        /**
         * 表类型
         */
        private final String type;

        WiringTypeG(byte g, String type) {
            this.g = g;
            this.type = type;
        }

        public byte getG() {
            return g;
        }

        public String getType() {
            return type;
        }

        public static WiringTypeG getByG(byte g) {
            for (WiringTypeG type : WiringTypeG.values()) {
                if (g == type.getG()) {
                    return type;
                }
            }
            return null;
        }
    }

    /**
     * 接线方式S
     */
    public enum WiringTypeS {
        /**
         * 直接接线
         */
        DIRECT((byte) 0, "直接接线"),
        /**
         * 经互感器接线
         */
        INDIRECT((byte) 1, "经互感器接线");

        private final byte s;
        /**
         * 表类型
         */
        private final String type;

        WiringTypeS(byte s, String type) {
            this.s = s;
            this.type = type;
        }

        public byte getS() {
            return s;
        }

        public String getType() {
            return type;
        }

        public static WiringTypeS getByS(byte s) {
            for (WiringTypeS type : WiringTypeS.values()) {
                if (s == type.getS()) {
                    return type;
                }
            }
            return null;
        }
    }

    /**
     * 抄表数据
     */
    @Data
    @ToString
    public static class Copy {
        /**
         * 抄读方式<br>
         * 00H表示自动中继；<br>
         * 01H表示固定中继；<br>
         * <s>02H表示读取集中器中保存的数据；</s><br>
         * <span color="red">注：定义集抄实时抄表的表序号和中继参数，抄读方式的 02H在 V1.5版本中不再支持。</span>
         */
        private byte copyType;
        /**
         * 集中器上传的水电气数
         */
        private int meterQty;
        /**
         * 集中器上传的水电气数（原始值）
         */
        private byte[] meterQtyRaw;
        /**
         * 表数据
         */
        private MeterData[] meterData;
        /**
         * 表数据（原始值）
         */
        private byte[] meterDataRaw;
    }

    /**
     * 表数据
     */
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class MeterData {

        private byte[] raw;
        /**
         * 表序号
         */
        private int seq;
        /**
         * 表序号（原始值）
         */
        private byte[] seqRaw;
        /**
         * 累计水量(4字节)
         */
        private BigDecimal totalFlow;
        /**
         * 累计水量(4字节)（原始值）
         */
        private byte[] totalFlowRaw;
        /**
         * 阀门状态(1字节，0开1关)
         */
        private byte valveState;
    }
}
